// 版权所有2012 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// MakeFunc实现。

package reflect

import (
	"internal/abi"
	"unsafe"
)

// makeFuncImpl是实现MakeFunc返回的函数
// 的闭包值。
// 此类型的前三个字必须与
// methodValue和runtime.reflectMethodValue保持同步。
// 任何变化都应反映在这三个方面。
type makeFuncImpl struct {
	makeFuncCtxt
	ftyp *funcType
	fn   func([]Value) []Value
}

// MakeFunc返回一个给定类型的新函数
// 该函数包装函数fn。调用时，新函数
// 执行以下操作：
// 
// -将其参数转换为一段值。
// /-运行结果：=fn（args）。
// /-以值片的形式返回结果，每个正式结果一个。
// 
// 实现fn可以假设参数值片
// 具有由typ给出的参数数量和类型。
// 如果typ描述了一个可变函数，则最终值本身就是
// 表示可变参数的切片，如在可变函数体中的
// 中。fn 
// 返回的结果值切片必须具有typ给定的结果数和类型。
// 
// Value.Call方法允许调用方调用类型化函数
// 的值；相反，MakeFunc允许调用方实现
// 一个类型化函数的值。
// 
// 文档的示例部分包括如何使用MakeFunc为不同类型构建交换函数的示例
// 说明。
// 
func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value {
	if typ.Kind() != Func {
		panic("reflect: call of MakeFunc with non-Func type")
	}

	t := typ.common()
	ftyp := (*funcType)(unsafe.Pointer(t))

	// 获取
	// 实际代码地址的间接Go func值（伪）。（Go func值是指向C函数指针的指针
	// https:
	dummy := makeFuncStub
	code := **(**uintptr)(unsafe.Pointer(&dummy))

	// makeFuncImpl包含一个堆栈映射供运行时使用
	_, _, abi := funcLayout(ftyp, nil)

	impl := &makeFuncImpl{
		makeFuncCtxt: makeFuncCtxt{
			fn:      code,
			stack:   abi.stackPtrs,
			argLen:  abi.stackCallArgsSize,
			regPtrs: abi.inRegPtrs,
		},
		ftyp: ftyp,
		fn:   fn,
	}

	return Value{t, unsafe.Pointer(impl), flag(Func)}
}

// Makefunstub是一个汇编函数，它是从MakeFunc返回的函数
// 的一半代码。它需要一个*callReflectFunc 
// 作为它的上下文寄存器，其任务是调用callReflect（ctxt，frame）
// 其中ctxt是上下文寄存器，frame是指向传入参数帧中第一个
// 字的指针。
func makeFuncStub()

// 此类型的前3个字必须与
// makeFuncImpl和runtime.reflectMethodValue保持同步。
// 所有更改都应反映在所有thr中ee.
type methodValue struct {
	makeFuncCtxt
	method int
	rcvr   Value
}

// makeMethodValue将v从方法值的rcvr+方法索引表示形式
// 转换为实际的方法func值，即
// 基本上是设置了特殊位的接收器值，转换为真正的
// func值-一个包含实际func的值。输出为
// 就软件包
// reflect的用户所知，在语义上等同于输入，但真正的func表示可以通过Convert and Interface and Assign之类的代码来处理
// 。
func makeMethodValue(op string, v Value) Value {
	if v.flag&flagMethod == 0 {
		panic("reflect: internal error: invalid use of makeMethodValue")
	}

	// 忽略flagMethod位，v描述接收方，而不是方法类型。
	fl := v.flag & (flagRO | flagAddr | flagIndir)
	fl |= flag(v.typ.Kind())
	rcvr := Value{v.typ, v.ptr, fl}

	// v.type return方法值的实际类型。
	ftyp := (*funcType)(unsafe.Pointer(v.Type().(*rtype)))

	// 间接Go func值（伪）以获取
	// 实际代码地址。（Go func值是指向C函数指针的指针
	// https:
	dummy := methodValueCall
	code := **(**uintptr)(unsafe.Pointer(&dummy))

	// methodValue包含一个堆栈映射，供运行时使用
	_, _, abi := funcLayout(ftyp, nil)
	fv := &methodValue{
		makeFuncCtxt: makeFuncCtxt{
			fn:      code,
			stack:   abi.stackPtrs,
			argLen:  abi.stackCallArgsSize,
			regPtrs: abi.inRegPtrs,
		},
		method: int(v.flag) >> flagMethodShift,
		rcvr:   rcvr,
	}

	// 如果方法不合适，则会导致死机。
	// 如果忽略此项，则在调用过程中仍会发生死机，但我们希望face（）和其他操作将提前失败。
	methodReceiver(op, fv.rcvr, fv.method)

	return Value{&ftyp.rtype, unsafe.Pointer(fv), v.flag&flagRO | flag(Func)}
}

// methodValueCall是一个汇编函数，它是从makeMethodValue返回的函数的一半代码。它需要一个*methodValue 
// 作为其上下文寄存器，其任务是调用callMethod（ctxt，frame）
// 其中ctxt是上下文寄存器，frame是指向传入参数frame中第一个
// 字的指针。
func methodValueCall()

// 此结构必须与runtime.reflectMethodValue保持同步。
// 所有更改都应反映在这两个堆栈中。
type makeFuncCtxt struct {
	fn      uintptr
	stack   *bitVector // 两个堆栈a的ptrmaprgs和结果
	argLen  uintptr    // just args 
	regPtrs abi.IntArgRegBitmap
}

// moveMakeFuncArgPtrs使用ctxt.regPtrs将args.Ints中的整数指针参数
// 复制到args.Ptrs，GC可以在其中看到它们。
// 
// 这类似于reflectcallmove在运行时所做的操作，除了
// 这一点之外pens在返回路径上，而这发生在调用路径上。
// 
// nosplit因为指针被保存在args中的uintptr插槽中，所以
// 现在扫描堆栈可能会导致意外释放
// 内存。
// go:nosplit 
func moveMakeFuncArgPtrs(ctxt *makeFuncCtxt, args *abi.RegArgs) {
	for i, arg := range args.Ints {
		// 避免写入障碍！因为我们的写屏障将之前存在的
		// 放入队列，所以我们可能会将垃圾放入队列。
		if ctxt.regPtrs.Get(i) {
			*(*uintptr)(unsafe.Pointer(&args.Ptrs[i])) = arg
		} else {
			// 我们*必须*自己将这个空间归零，因为它是在
			// 汇编代码中定义的，GC将扫描这些指针。否则，这里会有垃圾。
			*(*uintptr)(unsafe.Pointer(&args.Ptrs[i])) = 0
		}
	}
}
